﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lab05
{
    interface IMatrixIterator
    {
        void First();
        void Next();
        bool IsDone { get; }
        MatrixElement Current { get; }
    }

    class DenseMatrixIterator : IMatrixIterator
    {
        private MatrixElement _current;
        private IMatrix _matrix;
        private bool _isDone;

        public DenseMatrixIterator(IMatrix matrix)
        {
            _matrix = matrix;
            if (_matrix.GetNumColumns() < 1 || _matrix.GetNumRows() < 1)
                throw new Exception("Empty matrix");
            _current = new MatrixElement();
            First();
        }

        public virtual void First()
        {
            _isDone = false;
            _current.I = 0;
            _current.J = 0;
            _current.Value = _matrix.GetValue(_current.I, _current.J);
        }

        public virtual void Next()
        {
            if (_current.J < _matrix.GetNumColumns() - 1)
            {
                _current.J++;
            }
            else
            {
                _current.J = 0;
                if (_current.I < _matrix.GetNumRows() - 1)
                {
                    _current.I++;
                }
                else
                {
                    _current.I = 0;
                    _isDone = true;
                }
            }
            _current.Value = _matrix.GetValue(_current.I, _current.J);
        }

        public bool IsDone
        {
            get { return _isDone; }
        }

        public MatrixElement Current
        {
            get { return _current; }
        }
    }

    class SparseMatrixIterator : DenseMatrixIterator
    {
        public SparseMatrixIterator(IMatrix matrix)
            : base(matrix)
        {
        }

        public override void First()
        {
            base.First();
            if (Current.Value == 0) Next();
        }

        public override void Next()
        {
            base.Next();
            if (!IsDone && Current.Value == 0) Next();
        }
    }


    class HorizontalMatrixGroupIterator : IMatrixIterator
    {
        private readonly HorizontalMatrixGroup _matrixGroup;
        private bool _isDone;
        private int _offset;
        private int _index;
        private IMatrixIterator _currentIterator;

        public HorizontalMatrixGroupIterator(HorizontalMatrixGroup matrixGroup)
        {
            _matrixGroup = matrixGroup;
            First();
        }

        public void First()
        {
            _index = 0;
            _currentIterator = _matrixGroup.GetSubmatrixIterator(_index);
            _offset = 0;
            _isDone = false;
        }

        public void Next()
        {
            _currentIterator.Next();
            if (_currentIterator.IsDone)
            {
                _offset += _matrixGroup.GetSubmatrixNumColumns(_index);
                _index++;
                try
                {
                    _currentIterator = _matrixGroup.GetSubmatrixIterator(_index);
                }
                catch(Exception)
                {
                    _isDone = true;
                }
            }
        }

        public bool IsDone
        {
            get { return _isDone; }
        }

        public MatrixElement Current
        {
            get
            {
                MatrixElement result = _currentIterator.Current;
                result.J += _offset;
                return result;
            }
        }
    }


    class VerticalMatrixGroupIterator : IMatrixIterator
    {
        private readonly VerticalMatrixGroup _matrixGroup;
        private bool _isDone;
        private int _offset;
        private int _index;
        private IMatrixIterator _currentIterator;

        public VerticalMatrixGroupIterator(VerticalMatrixGroup matrixGroup)
        {
            _matrixGroup = matrixGroup;
            First();
        }

        public void First()
        {
            _index = 0;
            _currentIterator = _matrixGroup.GetSubmatrixIterator(_index);
            _offset = 0;
            _isDone = false;
        }

        public void Next()
        {
            _currentIterator.Next();
            if (_currentIterator.IsDone)
            {
                _offset += _matrixGroup.GetSubmatrixNumRows(_index);
                _index++;
                try
                {
                    _currentIterator = _matrixGroup.GetSubmatrixIterator(_index);
                }
                catch (Exception)
                {
                    _isDone = true;
                }
            }
        }

        public bool IsDone
        {
            get { return _isDone; }
        }

        public MatrixElement Current
        {
            get
            {
                MatrixElement result = _currentIterator.Current;
                result.I += _offset;
                return result;
            }
        }
    }

}
